/******************************************************************************
 * Filename : uc_buf.c
 * Created  : 2009-10-01 by franco.yin
 * Description -
 * 
 ******************************************************************************/

#ifdef __cplusplus
extern "C" {
#endif

#include "uc_buf.h"

ucbuf_s *
ucbuf_new(int size)
{
	ucbuf_s *pBuf;

	if (size < 0) {
		return NULL;
	}

	pBuf = (ucbuf_s *)uc_malloc(sizeof(ucbuf_s) + sizeof(ucmsg_s) + size);
	if (!pBuf) {
		uc_err("malloc\n");
		return NULL;
	}

	pBuf->size = size + sizeof(ucmsg_s);
	pBuf->magic = UCBUF_MAGIC;
	pBuf->memory = (char *) pBuf + sizeof (ucbuf_s);
	pBuf->recvp = pBuf->memory;
	pBuf->sendp = pBuf->memory;
	pBuf->headp = (pBuf->memory + sizeof (ucmsg_s));
	pBuf->endp = (pBuf->memory + sizeof (ucmsg_s));

	return pBuf;
}

int
ucbuf_free(ucbuf_s *pBuf)
{
	if (pBuf->magic != UCBUF_MAGIC) {
		uc_err("invalid ucbuf\n");
		return ERROR;
	}

	uc_free(pBuf);
	return OK;
}

int
ucbuf_init(ucbuf_s *pBuf, ucmsg_s *pMsg, int buf_len, int used_len)
{
	if(pBuf == NULL || pMsg == NULL || used_len > buf_len) {
		uc_err("ucbuf_init error\n");
		return ERROR;
	}

	memset(pBuf, 0, sizeof(ucbuf_s));

	pBuf->size = buf_len + sizeof(ucmsg_s);
	pBuf->magic = UCBUF_MAGIC;
	pBuf->memory = (char *)pMsg;
	pBuf->recvp = (char *)pMsg;
	pBuf->sendp = (char *)pMsg;
	pBuf->headp = pBuf->memory + sizeof (ucmsg_s);
	pBuf->endp = pBuf->memory + sizeof (ucmsg_s) + used_len;

	return OK;
}

int
ucbuf_jump(ucbuf_s *pBuf, int len)
{
	if (!pBuf || len <= 0) {
		return ERROR;
	}

	if (pBuf->magic != UCBUF_MAGIC) {
		uc_err("invalid ucbuf\n");
		return ERROR;
	}

	pBuf->endp += len;
	return OK;
}

int
ucbuf_put(ucbuf_s *pBuf, char *mem, int len)
{
	if (!pBuf || !mem) {
		return ERROR;
	}

	if (pBuf->magic != UCBUF_MAGIC) {
		uc_err("invalid ucbuf\n");
		return ERROR;
	}
	
	if (pBuf->endp + len > pBuf->memory + pBuf->size) {
		return ERROR;
	}

	memcpy(pBuf->endp, mem, len);
	pBuf->endp += len;
	return OK;
}

int
ucbuf_move(ucbuf_s *pBuf, int len)
{
	if (!pBuf || len < 0) {
		return ERROR;
	}

	if (pBuf->magic != UCBUF_MAGIC) {
		uc_err("invalid ucbuf\n");
		return ERROR;
	}
	
	if (pBuf->endp + len > pBuf->memory + pBuf->size) {
		return ERROR;
	}

	pBuf->endp += len;
	return OK;
}

int
ucbuf_get(ucbuf_s *pBuf, char *mem, int len)
{
	if (!pBuf || !mem) {
		return ERROR;
	}

	if (pBuf->magic != UCBUF_MAGIC) {
		uc_err("invalid ucbuf\n");
		return ERROR;
	}

	if (pBuf->headp + len > pBuf->endp) {
		return ERROR;
	}

	memcpy(mem, pBuf->headp, len);
	pBuf->headp += len;
	return OK;
}

void
ucbuf_set_type(ucbuf_s *pBuf, UC_U32 type)
{
	ucmsg_s *pMsg = ucbuf_getmsgp(pBuf);
	pMsg->type = type;
}

int
ucbuf_insert(ucbuf_s *pBuf, int offset, char *mem, int len)
{
	if (!pBuf || !mem) {
		return ERROR;
	}

	if (pBuf->magic != UCBUF_MAGIC) {
		uc_err("invalid ucbuf\n");
		return ERROR;
	}

	if (pBuf->endp + len > pBuf->memory + pBuf->size) {
		return ERROR;
	}

	memmove(pBuf->headp + offset + len, pBuf->headp + offset,
		pBuf->endp - pBuf->headp - offset);
	memcpy(pBuf->headp + offset, mem, len);
	pBuf->endp += len;
	return OK;
}

int
ucbuf_dump(char *title, ucbuf_s *pBuf)
{
	char *p = NULL;
	ucmsg_s *pMsg = NULL;

	if (!pBuf) {
		return ERROR;
	}

	pMsg = ucbuf_getmsgp(pBuf);
	
	uc_p("%s: \n", title);
	uc_p("    type:%d\n", pMsg->type);
	uc_p("    len:%d\n\n", pMsg->len);
	
	uc_p("    head: ");
	for (p = pBuf->memory; p < (pBuf->memory + sizeof(ucmsg_s)); p++) {
		uc_p("%02X,", (unsigned char) (*p));
	}
	uc_p("\n");
	uc_p("    data: ");
	
	for (p = pBuf->memory + sizeof(ucmsg_s); p < pBuf->endp; p++) {
		uc_p("%02X,", (unsigned char) (*p));
	}
	uc_p("\n\n");

	return OK;
}

int
ucbuf_write(int fd, ucbuf_s *pBuf)
{
	int len;

	if ((fd <= 0) || !pBuf) {
		return 0;
	}

	if (pBuf->magic != UCBUF_MAGIC) {
		uc_err("invalid ucbuf\n");
		return ERROR;
	}

	len = write(fd, pBuf->sendp, ucbuf_len_send(pBuf));
	if (len <= 0) {
		if (errno == EAGAIN) {
			return -EAGAIN;
		}
		return len;
	}

	pBuf->sendp += len;
	return len;
}

int
ucbuf_send(int fd, ucbuf_s *pBuf)
{
	int len;

	if ((fd <= 0) || !pBuf) {
		return 0;
	}

	if (pBuf->magic != UCBUF_MAGIC) {
		uc_err("invalid ucbuf\n");
		return ERROR;
	}

	len = ucsock_send(fd, pBuf->sendp, ucbuf_len_send(pBuf));
	if (len <= 0) {
		return len;
	}

	pBuf->sendp += len;
	return len;
}

int
ucbuf_sendn(int fd, ucbuf_s *pBuf, int flags, int timeout_ms)
{
	int len;

	if ((fd <= 0) || !pBuf) {
		return 0;
	}

	if (pBuf->magic != UCBUF_MAGIC) {
		uc_err("invalid ucbuf\n");
		return ERROR;
	}

	len = ucsock_sendn(fd, pBuf->sendp, ucbuf_len_send(pBuf), timeout_ms);
	if (len <= 0)
		return len;

	pBuf->sendp += len;
	return len;
}

int
ucbuf_sendto(int fd, ucbuf_s *pBuf, SA_IN *to)
{
	int len;

	if ((fd <= 0) || !pBuf) {
		return 0;
	}

	if (pBuf->magic != UCBUF_MAGIC) {
		uc_err("invalid ucbuf\n");
		return ERROR;
	}

	len = ucsock_sendto(fd, pBuf->sendp, ucbuf_len_send(pBuf), to);
	if (len <= 0) {
		return len;
	}

	pBuf->sendp += len;
	return len;
}

int
ucbuf_recv(int fd, ucbuf_s *pBuf)
{
	int len;

	if ((fd <= 0) || !pBuf) {
		return 0;
	}

	if (pBuf->magic != UCBUF_MAGIC) {
		uc_err("invalid ucbuf\n");
		return ERROR;
	}

	len = ucsock_recv(fd, pBuf->recvp, ucbuf_getleftsize_recv(pBuf));
	if (len <= 0) {
		return len;
	}

	pBuf->recvp += len;
	return len;
}

int
ucbuf_read(int fd, ucbuf_s *pBuf)
{
	int len;

	if ((fd <= 0) || !pBuf) {
		return 0;
	}

	if (pBuf->magic != UCBUF_MAGIC) {
		uc_err("invalid ucbuf\n");
		return ERROR;
	}

	len = read(fd, pBuf->recvp, ucbuf_getleftsize_recv(pBuf));
	if (len <= 0) {
		if (errno == EAGAIN) {
			return -EAGAIN;
		}
		return len;
	}

	pBuf->recvp += len;
	return len;
}

int
ucbuf_recvfrom(int fd, ucbuf_s *pBuf, SA_IN *from)
{
	int len = 0;
	socklen_t addrlen = sizeof(SA_IN);

	if ((fd <= 0) || !pBuf) {
		return ERROR;
	}

	if (pBuf->magic != UCBUF_MAGIC) {
		uc_err("invalid ucbuf\n");
		return ERROR;
	}

	len = ucsock_recvfrom(fd, pBuf->recvp, ucbuf_getleftsize_recv(pBuf), from, &addrlen);
	if (len <= 0) {
		return len;
	}

	pBuf->recvp += len;
	return len;
}

int
ucbuf_recvn(int fd, ucbuf_s *pBuf, int n, int timeout_ms)
{
	int len = 0;

	if(n <= 0) {
		return 0;
	}

	if ((fd <= 0) || !pBuf) {
		return ERROR;
	}

	if (pBuf->magic != UCBUF_MAGIC) {
		uc_err("invalid ucbuf\n");
		return ERROR;
	}

	if (n > ucbuf_getleftsize_recv(pBuf)) {
		return ERROR;
	}

	len = ucsock_recvn(fd, pBuf->recvp, n, timeout_ms);
	if (len <= 0) {
		return len;
	}

	pBuf->recvp += len;
	return len;
}

int
ucbuf_send_msg(int fd, ucbuf_s *pBuf, int timeout_ms)
{
	ucmsg_s *pMsg = NULL; 

	if (pBuf == NULL || (pBuf->magic != UCBUF_MAGIC)) {
		uc_err("invalid ucbuf\n");
		return ERROR;
	}

	pMsg = (ucmsg_s *)pBuf->memory;
	pMsg->len = ucbuf_len(pBuf);
	ucmsg_hton(pMsg);

	return ucbuf_sendn(fd, pBuf, 0, timeout_ms);
}

int
ucbuf_sendto_msg(int fd, ucbuf_s *pBuf, SA_IN *dest)
{
	ucmsg_s *pMsg = NULL; 

	if (pBuf && (pBuf->magic != UCBUF_MAGIC)) {
		uc_err("invalid ucbuf\n");
		return ERROR;
	}

	pMsg = (ucmsg_s *)pBuf->memory;
	pMsg->len = ucbuf_len(pBuf);
	
	ucmsg_hton(pMsg);

	return ucbuf_sendto(fd, pBuf, dest);
}

int
ucbuf_recvfrom_msg(int fd, ucbuf_s *pBuf, SA_IN *from)
{
	int ret = 0;
	ucmsg_s *pMsg = NULL;

	ret = ucbuf_recvfrom(fd, pBuf, from);
	if (ret <= 0) {
		if(ret < 0) {
			uc_err("ucbuf_recvfrom_msg recv message head error[%d:%s]\n", errno, strerror(errno));
		}
		return ERROR;
	}

	pMsg = (ucmsg_s *)pBuf->memory;

	ucmsg_ntoh(pMsg);
	
	pBuf->endp = pBuf->recvp;

	return ret;		
}

int
ucbuf_recv_msg(int fd, ucbuf_s *pBuf, int timeout_ms)
{
	int ret = 0;
	ucmsg_s *pMsg = NULL; 

	ret = ucbuf_recvn(fd, pBuf, sizeof(ucmsg_s), timeout_ms);
	if (ret <= 0) {
		if(ret < 0) {
			uc_err("ucbuf_recv_msg recv message head error[%d:%s]\n", errno, strerror(errno));
		}
		return ERROR;
	}

	pMsg = (ucmsg_s *)pBuf->memory;
	ucmsg_ntoh(pMsg);

	ret = ucbuf_recvn(fd, pBuf, pMsg->len, timeout_ms);
	if (ret < 0) {
		uc_err("recv message data error msg type[%d] len[%d]\n", pMsg->type, pMsg->len);
		return ERROR;
	}
	pBuf->endp = pBuf->recvp;
//	ucbuf_dump("ucbuf_recv_msg", pBuf);

	return ret + sizeof (ucmsg_s);
}


ucbuf_s *
ucbuf_recv_new_msg(int fd, int timeout_ms)
{
	int ret = 0;
	ucmsg_s msg;
	ucbuf_s *pBuf = NULL;

	ret = ucsock_recvn(fd, (char *)&msg, sizeof(ucmsg_s), timeout_ms);
	if (ret <= 0) {
		if(ret < 0) {
			uc_err("ucbuf_recv_new_msg recv message head error[%d:%s]\n", errno, strerror(errno));
		}
		return NULL;
	}
	ucmsg_ntoh(&msg);

	pBuf = ucbuf_new(msg.len);
	if (pBuf == NULL) {
		uc_err("ucbuf_recv_new_msg malloc error type[%d] len[%d]\n", msg.type, msg.len);
		return NULL;
	}

	memcpy(pBuf->memory, &msg, sizeof(ucmsg_s));
	pBuf->recvp = pBuf->memory + sizeof(ucmsg_s);
	
	ret = ucbuf_recvn(fd, pBuf, msg.len, timeout_ms);
	if (ret < 0) {
		uc_err("recv message data error\n");
		ucbuf_free(pBuf);
		return NULL;
	}
	pBuf->endp = pBuf->recvp;

	return pBuf;
}

int
ucbuf_send_recv_msg(int fd, ucbuf_s *pBuf, int timeout_ms) 
{
	int ret = OK;
	
	ret = ucbuf_send_msg(fd, pBuf, timeout_ms);	
	if (ret <= 0) {
		if(ret < 0) {
			uc_err("send message data error\n");
		}
		return ERROR;
	}
	
	ucbuf_reset(pBuf);	

	return ucbuf_recv_msg(fd, pBuf, timeout_ms);
}

void
ucbuf_clean_msg_list(uclist_s *pList) 
{
	ucnode_s *pNode = NULL;
	ucnode_s *pTmp = NULL;
	ucbuf_s *pBuf = NULL;

	uclist_for_each_safe(pNode, pTmp, pList) {
		pBuf = uclist_entry(pNode, ucbuf_s, node_list);
		uclist_del(pNode);
		uc_free(pBuf);
	}
}

#ifdef __cplusplus
}
#endif
